package org.smartstruts.action;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.digester.Digester;
import org.apache.commons.digester.xmlrules.DigesterLoader;
import org.apache.log4j.Logger;
import org.smartstruts.Globals;
import org.smartstruts.config.ActionConfig;
import org.smartstruts.config.FormBeanConfig;
import org.smartstruts.config.ForwardConfig;
import org.smartstruts.config.ModuleConfig;

/**
 * 单实例多线程前端控制器
 * 
 * @author Wangpeihu
 * @category category
 * @version 1.0
 */
public class ActionServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	private static final String prefix = "/do";

	private ModuleConfig moduleConfig;

	/** 用于缓存Action */
	private final Map<String, Action> actions = Collections
			.synchronizedMap(new HashMap<String, Action>());

	/** 用户缓存用户的资源文件 */
	private final Map<Locale, ResourceBundle> resourceBoundles = Collections
			.synchronizedMap(new HashMap<Locale, ResourceBundle>());

	/** 日志处理对象 */
	private final Logger logger = Logger.getLogger(ActionServlet.class);

	/**
	 * 初始化，将配置文件加载
	 */
	@Override
	public void init() throws ServletException {
		try {
			Digester digester = DigesterLoader
					.createDigester(ActionServlet.class.getClassLoader()
							.getResource("org/smartstruts/config/rule.xml"));
			moduleConfig = new ModuleConfig();

			/** 将rule里面的配置信息装到moduleCfg里面 */
			digester.push(moduleConfig);

			digester.parse(ActionServlet.class.getClassLoader().getResource(
					"struts-config.xml"));

			logger.debug("初始化配置对象: \n" + moduleConfig);
		} catch (Exception e) {
			throw new ServletException(e);
		}
	}

	@Override
	public void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		try {

			/** 1.处理资源文件 */
			processResource(request, response);
			logger.debug("客户端IP：" + request.getRemoteAddr());
			logger.debug("客户端主机端口：" + request.getRemotePort());
			logger.debug("客户端主机名称：" + request.getRemoteHost());
			logger.debug("开始处理资源文件");

			/** 2.调用路径处理方法，得到路径 */
			String path = processPath(request, response);
			logger.debug("路径：" + path);

			/** 3.根据路径得到相应的配置对象， */
			ActionConfig actionCfg = moduleConfig.findActionConfig(path);
			logger.debug("配置对象：" + actionCfg);

			/** 4.执行表单提交任务 */
			ActionForm form = processForm(actionCfg, request, response);
			logger.debug("提交表单任务：" + form);

			/** 5.调用主执行方法，返回转发类型(是转发还是重定向) */
			String forwardName = processExecute(actionCfg, form, request,
					response);
			logger.debug("执行主要任务：" + forwardName);

			/** 6.调用转发(或重定向) */
			processForward(forwardName, actionCfg, request, response);
			logger.debug("转发(重定向)开始");
		} catch (Exception e) {
			throw new ServletException(e);
		}
	}

	/**
	 * 处理提交表单
	 * 
	 * @param actionCfg
	 * @param request
	 * @param response
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	private ActionForm processForm(ActionConfig actionCfg,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		/** 得到提交指令 */
		String formName = actionCfg.getName();
		ActionForm form = null;
		if (formName != null) {
			// request|session中查找
			String scope = actionCfg.getScope();
			/** 在request里面 */
			if ("request".equals(scope)) {
				form = (ActionForm) request.getAttribute(actionCfg
						.getAttribute());
				/** 不在request里面 */
			} else {
				form = (ActionForm) request.getSession().getAttribute(
						actionCfg.getAttribute());
			}
			/** first runing execute this code statement */
			if (form == null) {
				// 如果session|request中没有form就创建新的form对象
				FormBeanConfig formBeanCfg = moduleConfig
						.findFormBeanConfig(formName);
				if (formBeanCfg != null) {
					String formBeanType = formBeanCfg.getType();
					Class formBeanClass = Class.forName(formBeanType);
					form = (ActionForm) formBeanClass.newInstance();
					if ("request".equals(scope)) {
						request.setAttribute(actionCfg.getAttribute(), form);
					} else {
						request.getSession().setAttribute(
								actionCfg.getAttribute(), form);
					}
				}
			}
			BeanUtils.populate(form, request.getParameterMap());
			logger.debug("构建ActionForm: " + form);
		}
		return form;
	}

	/**
	 * 处理路径方法
	 * 
	 * @param request
	 * @param response
	 * @return
	 * @throws Exception
	 */
	private String processPath(HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		String uri = request.getRequestURI();
		String path = uri.substring(uri.indexOf(prefix) + prefix.length());
		return path;
	}

	/**
	 * 处理资源文件
	 * 
	 * @param request
	 * @param response
	 */
	private void processResource(HttpServletRequest request,
			HttpServletResponse response) {
		Locale locale = request.getLocale(); // 得到本地语言
		ResourceBundle rb = resourceBoundles.get(locale);
		if (null == rb) {
			/**
			 * 下面的resource的意思是在src目录下的<br>
			 * resource_en_US.properties/resource_en_CN.properites<br>
			 * 这两个文件的前半部分文件名，后半部分<br>
			 * 因为ResourceBundle这个类有这样的功能.<br>
			 */
			rb = ResourceBundle.getBundle("resource", locale);
			resourceBoundles.put(locale, rb);
		}
		request.setAttribute(Globals.KEY_MESSAGE_RESOURCES, rb);
		logger.debug("资源文件，本地语言" + locale);
	}

	/**
	 * 处理各种请求
	 * 
	 * @param actionCfg
	 * @param request
	 * @param response
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	private String processExecute(ActionConfig actionConfig, ActionForm form,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		Action action = actions.get(actionConfig.getPath());
		if (null == action) {
			String actionType = actionConfig.getType();
			Class actionClass = Class.forName(actionType);
			action = (Action) actionClass.newInstance();
			actions.put(actionConfig.getPath(), action);
		}

		String methodName = actionConfig.getMethod();

		/** 创建指定的方法 */
		Method method = action.getClass().getMethod(
				methodName,
				new Class[] { ActionForm.class, HttpServletRequest.class,
						HttpServletResponse.class });

		String forwardName = (String) method.invoke(action, new Object[] {
				form, request, response });
		logger.debug("调用了Action:" + action.toString());
		return forwardName;
	}

	/**
	 * 处理转发(或重定向)问题
	 * 
	 * @param forwardName
	 * @param actionCfg
	 * @param request
	 * @param response
	 * @throws Exception
	 */
	private void processForward(String forwardName, ActionConfig actionCfg,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		ForwardConfig forwardCfg = actionCfg.findForward(forwardName);

		/** 判断是否该转发(或重定向)配置对象是否存在？如果在子级别中没有，就用全局级别的 */
		if (forwardCfg == null) {
			forwardCfg = moduleConfig.findForward(forwardName);
		}
		if (forwardCfg != null) {
			String forwardPath = forwardCfg.getPath();
			if (forwardPath != null) {
				if (!forwardCfg.isRedirect()) {
					logger.debug("转发操作：" + forwardPath);
					request.getRequestDispatcher(forwardPath).forward(request,
							response);
				} else {
					String ctxPath = request.getContextPath(); // 获取该应用的应用名
					response.sendRedirect(ctxPath + forwardPath);
					logger.debug("重定向操作：" + forwardPath);
				}
			}
		}
	}
}